﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MapEditor.Model
{
    public class TreeNode
    {
        public static int SIZE_LEAF = 800;
        public Rect Rect;

        public int ID { get; set; }
        public List<GameObject> GameObjects { get; set; }
        public TreeNode Parent { get; set; }
        public List<TreeNode> Nodes { get; set; }

        public TreeNode(int Id, TreeNode Parent, Rect Rect)
        {
            this.ID = Id;
            this.Parent = Parent;
            this.Rect = Rect;

            GameObjects = new List<GameObject>();
            Nodes = new List<TreeNode>();
        }

        /// <summary>
        /// Check leaf node
        /// </summary>
        /// <returns></returns>
        public bool IsLeaf()
        {
            if (Rect == null)
            {
                return false;
            }

            double size = Rect.Width >= Rect.Height ? Rect.Width : Rect.Height;

            return size <= SIZE_LEAF;
        }

        /// <summary>
        /// Get child node from this Node
        /// </summary>
        public void CreateChildNode()
        {
            if (this.IsLeaf() || this.Nodes.Count == 4)
            {
                return;
            }
            if (Nodes == null)
            {
                Nodes = new List<TreeNode>();
            }

            Rect rect;
            TreeNode childNode;
            for (int i = 1; i < 5; i++)
            {
                rect = CreateChildRect(i);
                childNode = new TreeNode(ID * 4 + i, this, rect);
                Nodes.Add(childNode);
            }
        }

        /// <summary>
        /// Get child rect of Node
        /// </summary>
        /// <param name="ChildIndex"></param>
        /// <returns></returns>
        private Model.Rect CreateChildRect(int ChildIndex)
        {
            Rect rect = new Rect();
            rect.Width = this.Rect.Width / 2;
            rect.Height = this.Rect.Height / 2;
            rect.X = this.Rect.X;
            rect.Y = this.Rect.Y;
            switch (ChildIndex)
            {
                case 1:
                    rect.Y += rect.Height/2;
                    rect.X -= rect.Width / 2;
                    break;
                case 2:
                    rect.X += rect.Width/2;
                    rect.Y += rect.Height/2;
                    break;
                case 3:
                    rect.X -= rect.Width / 2;
                    rect.Y -= rect.Height / 2;
                    break;
                case 4:
                    rect.X += rect.Width / 2;
                    rect.Y -= rect.Height / 2;
                    break;
            }

            return rect;

        }

        /// <summary>
        /// Add game objet to node
        /// If this is leaf then add game object else find and add to leaf
        /// </summary>
        /// <param name="GameObject"></param>
        public void AddGameObject(GameObject GameObject)
        {
            if (this.IsLeaf())
            {
                if (GameObjects == null)
                {
                    GameObjects = new List<GameObject>();
                }
                this.GameObjects.Add(GameObject);
            }
            else
            {
                this.CreateChildNode();
                Rect rect = GameObject.Rect;
                Rect childRect;
                foreach (TreeNode node in Nodes)
                {
                    childRect = node.Rect;
                    //Check AABB
                    if (childRect.IsIntersectAll(childRect, rect))
                    {
                        node.AddGameObject(GameObject);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Remode Game object In node
        /// If this is leaf then remve else find leaf and remove
        /// </summary>
        /// <param name="GameObject"></param>
        public void RemoveObjectInNode(GameObject GameObject)
        {
            if (this.IsLeaf())
            {
                if (GameObject != null)
                {
                    GameObjects.Remove(GameObject);
                }
            }
            else
            {
                if (Nodes == null)
                {
                    return;
                }
                this.CreateChildNode();
                Rect rect = GameObject.Rect;
                Rect childRect;
                foreach (TreeNode node in Nodes)
                {
                    childRect = node.Rect;
                    if (childRect.IsIntersectAll(childRect, rect))
                    {
                        node.RemoveObjectInNode(GameObject);
                    }
                }

            }
        }

        private void _serialize(ref String result)
        {
            result += "\n" + this.ID.ToString() + " " + this.Rect.ToString() + " ";
        }

        public void serialize(ref String result)
        {
            this._serialize(ref result);
            if (this.IsLeaf())
            {
                if (this.GameObjects != null)
                {
                    foreach (GameObject obj in this.GameObjects)
                    {
                        result += obj.Id.ToString() + " ";
                    }
                    result.TrimEnd();
                }
            }
            else
            {
                if (this.Nodes.Count == 0)
                {
                    this.CreateChildNode();
                    
                }
                foreach (TreeNode node in this.Nodes)
                {
                    node.serialize(ref result);
                }
            }
        }

    }
}
